home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 264_01 / mv.c < prev    next >
Text File  |  1980-01-01  |  9KB  |  410 lines

  1. /*
  2.  * mv - move files and directories
  3.  *
  4.  * Usage: mv [-fiv] [-] file file
  5.  *        mv [-fiv] [-] file... dir
  6.  *        mv [-fiv] [-] dir dir
  7.  * Destination can be a drive, a directory, or a filename.
  8.  * Makes only minimal effort to stop you from destroying a file
  9.  * by moving it onto itself.  To move directory trees recursively, use
  10.  * cp -r and rm -r.
  11.  *
  12.  * Flags:
  13.  * -    interpret following arguments starting with '-' as filenames
  14.  * -f   force; don't report errors, override readonly attribute
  15.  * -i   interactive; asks whether to overwrite existing files
  16.  * -v   verbose; display name of each file as it's moved
  17.  *
  18.  * This program is in the public domain.
  19.  * David MacKenzie
  20.  * 6522 Elgin Lane
  21.  * Bethesda, MD 20817
  22.  *
  23.  * Latest revision: 05/08/88
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <stat.h>
  28. #include <fcntl.h>
  29. #include <ctype.h>
  30. #ifndef tolower(c)
  31. #define tolower(c) _tolower(c)
  32. #endif
  33.  
  34. /* Values for the first argument of ftime(). */
  35. #define FT_GET 0        /* Return date/time. */
  36. #define FT_SET 1        /* Set date/time to third argument. */
  37.  
  38. #define COPYSIZE (1024 * 16)    /* Number of bytes to copy at once. */
  39.  
  40. #define MAXPATH 125        /* Arbitrary internal limit on path lengths. */
  41.  
  42. int     fflag = 0;        /* Force success? */
  43. int     iflag = 0;        /* Interactive (query before copying)? */
  44. int     vflag = 0;        /* Verbose (display names as copied)? */
  45.  
  46. struct stat ssbuf, dsbuf, tsbuf;/* Source, destination, temp. */
  47.  
  48. _main(argc, argv)
  49.     int     argc;
  50.     char  **argv;
  51. {
  52.     void    usage(), mvdir(), mvfile();
  53.     int     optind;        /* Loop index. */
  54.  
  55.     for (optind = 1; optind < argc && *argv[optind] == '-'; ++optind) {
  56.     if (argv[optind][1] == 0)
  57.         break;
  58.     else
  59.         while (*++argv[optind])
  60.         switch (*argv[optind]) {
  61.         case 'f':
  62.             fflag = 1;
  63.             break;
  64.         case 'i':
  65.             iflag = 1;
  66.             break;
  67.         case 'v':
  68.             vflag = 1;
  69.             break;
  70.         default:
  71.             usage();
  72.             break;
  73.         }
  74.     }
  75.  
  76.     if (optind == argc || optind == argc - 1)
  77.     usage();
  78.  
  79.     /* Check type of first source argument. */
  80.     if (dstat(argv[optind], &ssbuf) == 0 && isdir(&ssbuf)) {
  81.     /* Can only move one directory onto one new directory. */
  82.     if (optind != argc - 2) {
  83.         if (!fflag)
  84.         fprintf(stderr, "%s: Directory rename only\n", argv[optind]);
  85.         exit(0);
  86.     }
  87.     mvdir(argv[optind], argv[argc - 1]);
  88.     } else {
  89.     if (dstat(argv[argc - 1], &dsbuf) == 0 && !isdir(&dsbuf) &&
  90.         optind != argc - 2)
  91.         /* Can't move multiple files onto one file. */
  92.         usage();
  93.     for (; optind < argc - 1; ++optind)
  94.         mvfile(argv[optind], argv[argc - 1]);
  95.     }
  96.  
  97.     exit(0);
  98. }
  99.  
  100. /*
  101.  * Rename a directory.
  102.  */
  103. void
  104. mvdir(source, dest)
  105.     char   *source;
  106.     char   *dest;
  107. {
  108.     if (prep(source, dest))
  109.     return;
  110.     if (rename(source, dest) == -1)
  111.     /* Problem could be with either source or dest. */
  112.     perr("mv");
  113. }
  114.  
  115. /*
  116.  * Move a file.
  117.  * Source is a file, dest is either a file (existing or new) or a directory
  118.  * (which must exist).
  119.  */
  120. void
  121. mvfile(source, dest)
  122.     char   *source;
  123.     char   *dest;
  124. {
  125.     long    ftime();
  126.     char   *basename(), *concat(), *Malloc();
  127.     struct stat *statp;
  128.     char   *buf;        /* Copy buffer. */
  129.     int     in, out;        /* File descriptors. */
  130.     int     n;            /* Number of bytes read. */
  131.     int     cperr = 0;        /* Error during copy? */
  132.  
  133.     if (prep(source, dest))
  134.     return;
  135.  
  136.     if (dstat(source, &ssbuf) == -1) {
  137.     if (!fflag)
  138.         fprintf(stderr, "%s: File not found\n", source);
  139.     return;
  140.     }
  141.     if (isdir(&ssbuf)) {
  142.     if (!fflag)
  143.         fprintf(stderr, "%s: Directory rename only\n", source);
  144.     return;
  145.     }
  146.     statp = &dsbuf;
  147.     if (isdir(&dsbuf)) {
  148.     dest = concat(dest, basename(source));
  149.     if (dstat(dest, &tsbuf) == 0 && isdir(&tsbuf)) {
  150.         if (!fflag)
  151.         fprintf(stderr, "%s: Is a directory\n", dest);
  152.         return;
  153.     }
  154.     statp = &tsbuf;
  155.     }
  156.     if (!permit(dest, statp) || !writable(dest, statp))
  157.     return;
  158.  
  159.     /* Try the simple way first. */
  160.     if (rename(source, dest) != -1)
  161.     return;
  162.  
  163.     if ((in = open(source, O_RDONLY)) == -1) {
  164.     perr(source);
  165.     return;
  166.     }
  167.     if ((out = open(dest, O_WRONLY | O_CREAT | O_TRUNC)) == -1) {
  168.     perr(dest);
  169.     (void) close(in);
  170.     return;
  171.     }
  172.     buf = Malloc(COPYSIZE);
  173.     while ((n = read(in, buf, COPYSIZE)) > 0)
  174.     if (write(out, buf, n) != n)
  175.         break;
  176.  
  177.     (void) free(buf);
  178.     /* Preserve readonly and other modes. */
  179.     if (chmod(dest, ssbuf.st_attr) == -1)
  180.     perr("chmod");
  181.     /* Set date and time of dest to those of source. */
  182.     if (ftime(FT_SET, out, ssbuf.st_mtime))
  183.     perr("ftime");
  184.  
  185.     if (close(in) == -1 || n < 0) {
  186.     if (!fflag)
  187.         fprintf(stderr, "%s: Read error\n", source);
  188.     cperr = 1;
  189.     }
  190.     if (close(out) == -1 || n > 0) {
  191.     if (!fflag)
  192.         fprintf(stderr, "%s: Write error\n", dest);
  193.     cperr = 1;
  194.     }
  195.     if (cperr) {
  196.     (void) unlink(dest);
  197.     return;
  198.     }
  199.     if (fflag && (ssbuf.st_attr & ST_RDONLY))
  200.     (void) chmod(source, ssbuf.st_attr & ~ST_RDONLY);
  201.     if (unlink(source) == -1)
  202.     perr("unlink");
  203. }
  204.  
  205. /*
  206.  * Common startup for both mvdir and mvfile.
  207.  * Return 0 if ok, 1 if error.
  208.  */
  209. prep(source, dest)
  210.     char   *source, *dest;
  211. {
  212.     char   *strtolower();
  213.  
  214.     (void) strtolower(source);
  215.     (void) strtolower(dest);
  216.     if (vflag)
  217.     printf("%s\n", source);
  218.  
  219.     if (!strcmp(source, dest)) {
  220.     if (!fflag)
  221.         fprintf(stderr, "%s and %s are identical\n", source, dest);
  222.     return 1;
  223.     }
  224.     return 0;
  225. }
  226.  
  227. /*
  228.  * Determine if overwriting destination file is permissible.
  229.  * Returns 1 if permission granted, 0 if not.
  230.  */
  231. permit(path, statp)
  232.     char   *path;
  233.     struct stat *statp;
  234. {
  235.     int     r;            /* Response character. */
  236.     int     c;            /* One character of input. */
  237.  
  238.     if (!iflag || fflag)
  239.     return 1;
  240.     /* If it doesn't exist, permission granted. */
  241.     if (statp->st_size == -1L || !isatty(0))
  242.     return 1;
  243.     fprintf(stderr, "overwrite %s? ", path);
  244.     fflush(stderr);
  245.     c = r = getchar();
  246.     while (c != '\n' && c != EOF)
  247.     c = getchar();
  248.     return r == 'y' || r == 'Y';
  249. }
  250.  
  251. /*
  252.  * Return 1 if write permission is granted on the path, 0 otherwise.
  253.  */
  254. writable(path, statp)
  255.     char   *path;
  256.     struct stat *statp;
  257. {
  258.     int     r;            /* Response character. */
  259.     int     c;            /* One character of input. */
  260.  
  261.     /* If it doesn't exist, it's writable. */
  262.     if (statp->st_size == -1L)
  263.     return 1;
  264.     if (statp->st_attr & ST_RDONLY) {
  265.     if (!fflag && isatty(0)) {
  266.         fprintf(stderr, "override protection for %s? ", path);
  267.         fflush(stderr);
  268.         c = r = getchar();
  269.         while (c != '\n' && c != EOF)
  270.         c = getchar();
  271.         if (r != 'y' && r != 'Y')
  272.         return 0;
  273.     }
  274.     return chmod(path, statp->st_attr & ~ST_RDONLY) != -1;
  275.     }
  276.     return 1;
  277. }
  278.  
  279. /*
  280.  * Convert a string to lowercase.  Return the argument.
  281.  */
  282. char   *
  283. strtolower(s)
  284.     char   *s;
  285. {
  286.     char   *t;
  287.  
  288.     for (t = s; *t; ++t)
  289.     if (isascii(*t) && isupper(*t) && *t != '_')
  290.         *t = tolower(*t);
  291.     return s;
  292. }
  293.  
  294. /*
  295.  * Return true if the path has the form "d:" or "\" or "d:\".
  296.  */
  297. isroot(p)
  298.     char   *p;
  299. {
  300.     int     i = strlen(p);
  301.  
  302.     return i == 2 && p[1] == ':' ||
  303.     !strcmp(p, "\\") ||
  304.     i == 3 && !strcmp(p + 1, ":\\");
  305. }
  306.  
  307. /*
  308.  * Return true if the path has the form:
  309.  * ".." or "d:.." or "." or "d:." or "\." or "d:\.".
  310.  */
  311. isdot(p)
  312.     char   *p;
  313. {
  314.     int     i = strlen(p);
  315.  
  316.     return !strcmp(p, "..") ||
  317.     i == 4 && !strcmp(p + 1, ":..") ||
  318.     !strcmp(p, ".") ||
  319.     i == 3 && !strcmp(p + 1, ":.") ||
  320.     !strcmp(p, "\\.") ||
  321.     i == 4 && !strcmp(p + 1, ":\\.");
  322. }
  323.  
  324. /*
  325.  * Like stat() but recognizes pseudo-directories:
  326.  * stat() can't find \, ., .., or \., so we check for them manually
  327.  * (\.. doesn't exist under MSDOS). 
  328.  * Sets statp->st_size to -1L if path not found.
  329.  * Returns: 0 = ok, -1 = path not found.
  330.  */
  331. dstat(path, statp)
  332.     char   *path;
  333.     struct stat *statp;
  334. {
  335.     if (isroot(path) || isdot(path)) {
  336.     statp->st_attr = ST_DIRECT;
  337.     return 0;
  338.     } else if (stat(path, statp) == -1) {
  339.     statp->st_size = -1L;
  340.     return -1;
  341.     } else
  342.     return 0;
  343. }
  344.  
  345. /*
  346.  * Return tr